package com.hb.core.filter;

import com.google.common.collect.Sets;
import com.hb.application.cache.TokenCache;
import com.hb.core.context.CurrentAccountHolder;
import com.hb.core.model.SecurityClient;
import com.hb.core.oauth2.AuthContextUtils;
import com.hb.domain.user.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHeaders;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Set;

/**
 * @description: 负责将当前用户信息放入线程本地变量
 * 优先级不高，但早于所有的拦截器
 * @projectName: zhb-frame-build
 * @author: zhouhaizhi
 * @createTime: 2021/8/27 11:19
 * @version:1.0
 */
@Slf4j
public class AccountContextFilter implements Filter {

    private static final String[] EXCLUDE_URL = new String[]{"/oauth/token"};

    private static final String TOKEN_PARAM = "token";

    private static final String INIT_PARAMETER_KEY = "exclude-uri";

    private static final int IP_ADDRESS_LENGTH = 15;

    private static Set<String> EXCLUDE_URL_SET;

    private static final String REMOTE_IP_SEPARATOR = ",";

    private ISysUserService sysUserService;

    private TokenCache tokenCache;

    public AccountContextFilter(ISysUserService sysUserService, TokenCache tokenCache) {
        this.sysUserService = sysUserService;
        this.tokenCache = tokenCache;
    }


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String excludeUri = filterConfig.getServletContext().getInitParameter(INIT_PARAMETER_KEY);
        String[] excludeUris;
        if (StringUtils.isNotEmpty(excludeUri)) {
            excludeUris = excludeUri.split(",");
        } else {
            excludeUris = EXCLUDE_URL;
        }
        EXCLUDE_URL_SET = Sets.newHashSet(excludeUris);
        if (log.isDebugEnabled()) {
            log.debug("[authContextFilter]初始化");
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //排除特殊链接
        if (EXCLUDE_URL_SET.contains(((HttpServletRequest) servletRequest).getRequestURI())) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        String token = ((HttpServletRequest) servletRequest).getHeader(HttpHeaders.AUTHORIZATION);
        if (StringUtils.isEmpty(token)) {
            token = servletRequest.getParameter(TOKEN_PARAM);
        }
        if (StringUtils.isEmpty(token)) {
            if (log.isDebugEnabled()) {
                log.debug("当前请求没有携带token");
            }
        }
        boolean hasToken = StringUtils.isNotEmpty(token);
        //有token的话就依赖token获取账户信息并放入线程本地变量
        if (hasToken) {
            try {
                SecurityClient inCompleteClient = AuthContextUtils.decodeToken(token);
                if (inCompleteClient == null) {
                    log.warn("token无法解析，token:{}", token);
                    return;
                }
                if (StringUtils.isNotBlank(tokenCache.getToken(token))) {
                    log.warn("token:{} 已经失效", token);
                    return;
                }
                SecurityClient completeClient = sysUserService.getSecurityClientByLoginName(inCompleteClient.getClientId());
                if (null == completeClient) {
                    log.warn("token不合法，token:{}", token);
                    return;
                }
                completeClient.setSourceIp(getSourceIp(servletRequest));
                CurrentAccountHolder.init();
                CurrentAccountHolder.set(completeClient);
            } catch (Exception e) {
                log.warn("token无法解析，原因：{}，token:{}", e.getMessage(), token);
            }
        }
        filterChain.doFilter(servletRequest,servletResponse);
        //有token的话清理线程本地变量
        if (hasToken) {
            CurrentAccountHolder.remove();
        }
    }

    /**
     *@描述 从请求头中获取来源Ip
     *@创建人 zhouhaizhi
     *@创建时间 2021/8/27 14:55
     *@参数 
     *@返回值 
     *@修改人和其它信息
     */
    private String getSourceIp(ServletRequest request){
        final String unknown = "unknown";
        final String xffHeaderKey = "X-Forwarded-For";
        String ipAddress = ((HttpServletRequest) request).getHeader(xffHeaderKey);
        if (StringUtils.isBlank(ipAddress) || unknown.equalsIgnoreCase(ipAddress)) {
            // Proxy-Client-IP: apache 服务代理
            final String apacheProxyHeaderKey = "Proxy-Client-IP";
            ipAddress = ((HttpServletRequest) request).getHeader(apacheProxyHeaderKey);
        }
        if (StringUtils.isBlank(ipAddress) || unknown.equalsIgnoreCase(ipAddress)) {
            // weblogic 服务代理
            final String webLogicHeaderKey = "WL-Proxy-Client-IP";
            ipAddress = ((HttpServletRequest) request).getHeader(webLogicHeaderKey);
        }
        if (StringUtils.isBlank(ipAddress) || unknown.equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            final String ipv4LocalAddress = "127.0.0.1";
            final String ipv6LocalAddress = "0:0:0:0:0:0:0:1";
            if (ipv4LocalAddress.equals(ipAddress) || ipv6LocalAddress.equals(ipAddress)) {
                //根据网卡读取本地配置ip
                try {
                    InetAddress inet = InetAddress.getLocalHost();
                    ipAddress = inet.getHostAddress();
                }catch (UnknownHostException e){
                    return StringUtils.EMPTY;
                }
            }
        }
        //对于通过多个代理的情况，第一个ip为客户端真是ip,多个ip按照‘,’分割
        if (ipAddress != null && ipAddress.length() > IP_ADDRESS_LENGTH) {
            if (ipAddress.indexOf(REMOTE_IP_SEPARATOR) > 0) {
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(REMOTE_IP_SEPARATOR));
            }
        }
        if (ipAddress != null) {
            ipAddress = ipAddress.trim();
        }
        return ipAddress;
    }

    @Override
    public void destroy(){
        if (log.isDebugEnabled()) {
            log.debug("[authContextFilter]销毁");
        }
    }
}
